use core::{ ptr, mem, convert::TryFrom };

pub mod protocol;
pub mod util;

pub const MAGIC_LENGTH: usize = 4;
pub const USER_NAME_LENGTH: usize = 20;
pub const HEADER_LENGTH: usize = MAGIC_LENGTH + USER_NAME_LENGTH + 1;

pub struct Header {
    _magic: [u8; MAGIC_LENGTH],
    user_name: [u8; USER_NAME_LENGTH],
    op: u8,
}

impl TryFrom<&[u8]> for Header {
    type Error = &'static str;

    fn try_from(bytes: &[u8]) -> Result<Self, Self::Error> {
         if bytes.len() < HEADER_LENGTH || !bytes.starts_with(protocol::MAGIC) {
            return Err("Not a Moyu header!");
         }
         let mut header: Header = unsafe { mem::zeroed() };
         unsafe { ptr::copy_nonoverlapping(bytes.as_ptr(), &mut header as *mut Header as *mut u8, 25) };

         Ok(header)
    }
}

impl Header {
    fn new() -> Self {
        let mut h = [0u8; HEADER_LENGTH];
        unsafe {
            ptr::copy_nonoverlapping(protocol::MAGIC.as_ptr(), h.as_mut_ptr(), MAGIC_LENGTH);
            mem::transmute(h)
        }
    }

    fn set_user_name(&mut self, user_name: &str) {
       let bname = user_name.as_bytes();

       unsafe {
           ptr::copy_nonoverlapping(bname.as_ptr(), self.user_name.as_mut_ptr(), usize::min(bname.len(), USER_NAME_LENGTH));
       }
    }

    pub fn get_user_name(&self) -> String {
        util::trim_nuls(&self.user_name)
    }

    fn set_op(&mut self, op: protocol::Operator) {
        self.op = op.into();
    }

    pub fn get_op(&self) -> protocol::Operator {
        protocol::Operator::from(self.op)
    }

    fn into_bytes(self) -> [u8; HEADER_LENGTH] {
        unsafe { mem::transmute(self) }
    }
}

#[derive(Clone, Debug)]
pub struct Packet {
    user_name: String,
    op: protocol::Operator,
    message: String,
}

impl TryFrom<&[u8]> for Packet {
    type Error = &'static str;
    
    fn try_from(bytes: &[u8]) -> Result<Self, Self::Error> {
        let header = Header::try_from(bytes)?;
        let message = util::trim_nuls(&bytes[HEADER_LENGTH..]);

        Ok(Self {
            user_name: header.get_user_name(),
            op: header.get_op(),
            message,
        })
    }
}

impl Packet {
    pub fn new(user_name: &str, op: protocol::Operator, msg: &str) -> Self {
        Self {
            user_name: String::from(user_name),
            op,
            message: String::from(msg),
        }
    }

    pub fn set_user_name(&mut self, name: &str) {
        self.user_name = String::from(name);
    }

    pub fn get_user_name(&self) -> &str {
        &self.user_name
    }

    pub fn set_op(&mut self, op: protocol::Operator) {
        self.op = op;
    }

    pub fn get_op(&self) -> protocol::Operator {
        self.op
    }

    pub fn set_message(&mut self, msg: &str) {
        self.message = String::from(msg)
    }

    pub fn get_message(&self) -> &str {
        &self.message
    }

    pub fn encode(&self) -> Vec<u8> {
        let mut header = Header::new();
        header.set_user_name(&self.user_name);
        header.set_op(self.op);
        
        let mut p = Vec::from(header.into_bytes());

        p.extend_from_slice(&self.message.as_bytes());

        p
    }
}
